… … … I relished the thought of
… … … … creating a “simple” open-sourced Flutter app in around three days.
tl;dr: The functional design took a week;
— read, ‘how three days turned into near three months’.
On the bright side, ChatGPT and GitHub Copilot reduced the coding time by about 20-30%. What would likely have taken ~3-4 months was completed in ten weeks.
Granted, a prototype isn’t meant to be a “final” product, and although there are still some features in the queue, I believe the ‘prototype’ portion of the effort could’ve been called ‘done’ weeks ago. But then, I love a quality working product, and, this project was as much about Flutter development practice with OOP and SOLID as it was about practical prototyping. So while I aim to continue advancing my prototyping prowess, don’t we all want what we’re working on to be the best we can make it?
“whatever is worth doing at all, is worth doing well”
Philip Stanhope, 4th Earl of Chesterfield, 1746
In the end, I do aim to continue practicing prototyping (and better learning where that cutoff line is). The good news is that aside from the more critical time element of the project, and knowing I’m only a year into learning OOP and design patterns, the resulting code did indeed end up with a few intentional OOP-based structures. Plus, I can soundly say my future self will have much less anxiety going back into this project than my recent refactored apps.
Project One: Tic Tac Tuple
The first app I opted to begin practicing practical prototyping with was Tic Tac Toe. The project was later renamed to Tic Tac Tuple in considering the game board supports three sized grids; 3×3, 4×4, and 5×5, as well as 1-4 players. And being the art of calculating winning tiles within those grids involved chunking the tiles into tuples, thus, the name change made sense.
As referenced earlier, the architectural design of the app’s functionality took a week. Knowing when the design was “done” was a challenge in and of itself. But as with all projects, time and effort helped morph that design into class structures, and further down into properties and methods.
As the design neared completion, I was eager to start into coding via TDD. Admittedly, however, it didn’t last long. As the project wore on, testing got done mostly after coding patterns were resolved, which is how it was done for over a year at my last Flutter position. For some, TDD could well be an ever-elusive pipe dream — at least without a paradigm shift in thinking such as that between procedural and OOP.
The App
Introducing: Tic Tac Bot
Also referenced earlier, the Tic Tac Tuple board can be a grid of either 3×3, 4×4, or 5×5. And with an attempt at OOP for this feature, in theory, a 6×6 and 7×7 could be added . . . quite . . . easily? No matter the case, the contrast in simplicity is stark in comparison if it had been done with JavaScript and PHP, as I would have back in the day (i.e. 3+ years ago). The number of players and the player symbol markers could be added to as well.
When playing with only one player, a friendly opponent, Tic Tac Bot, will join you. For some really great news,
if you’ve ever wanted to win at Tic Tac Toe, now is your chance!
As referenced in the ‘Current Shortcomings’ section below, currently TicTac is only prepared to defend against wins. What that means, and what the future feature list below provides insights into, is that TicTac does not play to win, and what’s more, does not even yet check for blocks between split tiles. Those are actually future features that are slated to be bot-level difficulty preferences.
Despite Tic Tac Toe being one of the simplest games to consider creating, in hindsight, there’s a LOT that goes into creating a game (or any app) that goes beyond the creation of the game itself. Perhaps a simple 3×3 grid with 2 players may have been an easier challenge, but then there’s still the game flow design, UI design and theming, images and icons, app- and local-level state management, storage, navigation, etc., etc.. Most of the initial design aspects even had to be changed along the way — so much so I had to remove the ‘initial design’ notes from the top of the main class files (although meant as insights into the designing of the app, those notes were better suited for the readme anyways).
Current Shortcomings
- Untested on iOS.
- Landscape orientation may only play in portrait mode. Need to test out
MediaQuery.of(context).orientation
to see if it relies on layout builder constraints, which gets confused on smaller screens when the keyboard comes up thinking it switched to landscape.
Future Features
- Add a light background.
- Start with showing a random image (of a few available in the
assets\images\backgrounds
folder). - Later, add a background option for solid colors (maybe use a palette plugin).
- Start with showing a random image (of a few available in the
- Create a local logger to replace all prints, logs, and debugPrints.
- Create a preferences panel — possibly a bottom sheet.
- Enhance bot play: Add bot-level difficultly playing options:
- Allow Bot play to also look for its own longest range (offensive plays).
- Without this check (current) it only plays defensively.
// Potential source: lib.src.data.models.bot_play.BotPlay.findBestTileIndex
- Add ‘split tile’ checks for blocking (or winning) moves.
- Add option to ‘Clear All Games’ (there’s an example usage in
app_load_bootstrap
). - The data is already available for the following features:
- Add: View all player wins (leaderboard)
- Every
playerId
inScorebookData.endGameScores
has aPlayerData
inScorebookData.allPlayers
.
- Every
- Add: View previously played games
- Stored in
ScorebookData.allGames
.
- Stored in
- Add: View all player wins (leaderboard)
- Enhance bot play: Add bot-level difficultly playing options:
Appreciations
I truly appreciate Flutter Bloc‘s separation of concerns by way of having its own event
and state
environments and flows. When I first developed a few apps with Provider using ChangeNotifiers
, data and services became entangled. Flutter Bloc enforces data separation, and the repository pattern fully complements this separation. Learning the repository pattern with Flutter Bloc has been the most illuminating, or even freeing, for understanding the overall data flow. Granted I still call some methods directly as opposed to using a bloc or cubit, but the overall architectural scheme for the entire app is so much easier to understand than the aforementioned JavaScript and PHP approach.
I also really appreciate Mocktail if for nothing more than its non-use of code generation using a build runner (as Mockito does). As these projects grow, the time in having to run the build runner when classes are added or updated adds up. Additionally, Mocktail reduces the frustration of having to even be reminded to run the build runner.
In summary…
When trying to learn dynamic systems when working solo, I do miss the expert insights you get from working alongside inherently talented software engineers who “just get it”. They can see when you should have used composition over inheritance, when you break SOLID, and they can help isolate and break down lengthy logic conditions. In that light, I do try to tailor my GPT requests to help guide me in the right directions, but alas, because Dart is a multi-paradigm language, deriving pure OOP solutions can be challenging. This is why I’m always open to input and feedback — learning is as necessary as breathing (for me anyways).
I kept a fairly detailed log of the first month or so of development, which can be found in the source code.
Source code: https://github.com/KDCinfo/dev-play/tree/main/tic-tac-tuple
Cheers!
– Keith | https://keithdc.com
1 thought on “Fun with Practical Prototyping: Project One”